#ifndef _CERTIFICATE_HPP
#define _CERTIFICATE_HPP 1

#include <stdint.h>
#include <boost/shared_ptr.hpp>

#include "../network/BaseNode.hpp"

#include "../OrderParameters.hpp"

#include "../Parameters.hpp"

#include "../messages/HistoryDigest.hpp"
#include "../messages/CommandBatch.hpp"
#include "../messages/NonDeterminism.hpp"
#include "../messages/Digest.hpp"
#include "../messages/CertificateEntry.hpp"
#include "../messages/NextBatch.hpp"
#include "../messages/CommittedNextBatch.hpp"
#include "../messages/SpeculativeNextBatch.hpp"
#include "../messages/TentativeNextBatch.hpp"
#include "../messages/PrePrepare.hpp"
#include "../messages/Prepare.hpp"
#include "../messages/Commit.hpp"

//#include <boost/thread.hpp>

namespace BFT
{
  namespace order
  {
    namespace statemanagement
    {
      class Certificate
      {
				
      public:
				
	Certificate(int _replicas);
				
	Certificate(int _replicas, bool _cp);
								
	boost::shared_ptr<BFT::order::messages::PrePrepare> getCachedPrePrepare();
				
	//get the digest of the preprepare
	boost::shared_ptr<BFT::messages::HistoryDigest> getHistory();
				
	boost::shared_ptr<BFT::messages::CommandBatch> getCommandBatch();
				
	boost::shared_ptr<BFT::messages::NonDeterminism> getNonDeterminism();
				
	boost::shared_ptr<BFT::messages::Digest> getEntryDigest();
				
	boost::shared_ptr<BFT::messages::NextBatch> getNextBatch(boost::shared_ptr<BFT::BaseNode>_bn, uint32_t _current_view);
				
	void setEntry(boost::shared_ptr<BFT::messages::CertificateEntry> _entry, uint32_t _seq);
				
	//add a preprepare that I sent
	bool addMyPrePrepare(boost::shared_ptr<BFT::order::messages::PrePrepare> _pp);
				
	//add a preprepare that somebody else sent
	bool addOtherPrePrepare(boost::shared_ptr<BFT::order::messages::PrePrepare> _pp);
				
	//add a prepare that somebody else sent
	bool addOtherPrepare(boost::shared_ptr<BFT::order::messages::Prepare> _p);
				
	//add a commit that I sent
	bool addMyCommit(boost::shared_ptr<BFT::order::messages::Commit> _c);
				
	//add a commit that somebody else sent
	bool addOtherCommit(boost::shared_ptr<BFT::order::messages::Commit> _c);

	void forceCommitted();
				
	void forcePrepared();

	bool isPrepared();
				
	int getPreparedCount();
				
	bool isCommitted();
			
	static int getFastQuorumSize();
				
	static int getQuorumSize();
				
	static int getSmallQuorumSize();
				
	//static int index(uint32_t replica);		
			
	boost::shared_ptr<BFT::order::messages::Prepare> getPrepare(); //sync
				
	//add a prepare that I sent
	bool addMyPrepare(boost::shared_ptr<BFT::order::messages::Prepare> _p); //sync

	boost::shared_ptr<BFT::order::messages::PrePrepare> getPrePrepare(); //inlined

	boost::shared_ptr<BFT::order::messages::Commit> getCommit(); //inlined
				
	void cachePrePrepare(boost::shared_ptr<BFT::order::messages::PrePrepare> _pp); //inlined
				
	std::vector<boost::shared_ptr<BFT::order::messages::Prepare> > getPrepareCache(); //inlined
				
	std::vector<boost::shared_ptr<BFT::order::messages::Commit> > getCommitCache(); //inlined
				
	uint32_t getSeqNo(); //inlined
				
	bool takeCP(); //inlined
				
	boost::shared_ptr<BFT::messages::CertificateEntry> getCertEntry();//inlined
				
	bool preparedBy(int _i); //inlined
				
	void cachePrepare(boost::shared_ptr<BFT::order::messages::Prepare> _p); //inlined
				
	void cacheCommit(boost::shared_ptr<BFT::order::messages::Commit> _c); //inlined
				
	bool isPrePrepared(); //inlined
				
	bool isClear(); //inlined
				
      protected:
				
	void clear(); //sync
				
	//preprepare that I sent
	boost::shared_ptr<BFT::order::messages::PrePrepare> preprepare;
				
	//collection of received prepares
	std::vector<bool> prepareMacs;
				
	//collection of received commits
	std::vector<bool> commitMacs;
				
	//the prepare I sent for this sequence number
	boost::shared_ptr<BFT::order::messages::Prepare> prepare;
				
	//the commit I sent for this sequence number
	boost::shared_ptr<BFT::order::messages::Commit> commit;
				
	//cache for prepare received out of order
	std::vector<boost::shared_ptr<BFT::order::messages::Prepare> > prepareCache;
				
	//cache for commit received out of order
	std::vector<boost::shared_ptr<BFT::order::messages::Commit> > commitCache;
				
	int32_t seqNo;
				
	bool takecp;
				
	boost::shared_ptr<BFT::messages::CertificateEntry> certEntry;
				
	boost::shared_ptr<BFT::messages::Digest> entryDigest;
				
	//next batch to be sent
	boost::shared_ptr<BFT::messages::NextBatch> nextBatch;
				
	//clear all the fields
	bool clear_flag;
				
	//int my_order_index;
				
      private:
				
	boost::mutex synchronize;
				
      }; //class certificate
			
      inline boost::shared_ptr<BFT::order::messages::PrePrepare> Certificate::getPrePrepare() {return preprepare;} 
			
      inline boost::shared_ptr<BFT::order::messages::Commit> Certificate::getCommit() {return commit;}
			
      inline void Certificate::cachePrePrepare(boost::shared_ptr<BFT::order::messages::PrePrepare> _pp) 
      { preprepare = _pp;}

      inline std::vector<boost::shared_ptr<BFT::order::messages::Prepare> > Certificate::getPrepareCache() 
      {return prepareCache;} 

      inline std::vector<boost::shared_ptr<BFT::order::messages::Commit> > Certificate::getCommitCache() 
      {return commitCache;} 

      inline uint32_t Certificate::getSeqNo() {return seqNo;}
			
      inline bool Certificate::takeCP() {return takecp;}
						
      inline boost::shared_ptr<BFT::messages::CertificateEntry> Certificate::getCertEntry() {return certEntry;}			
			
      inline bool Certificate::preparedBy(int _i) {return prepareMacs.at(_i);}
			
      inline void Certificate::cachePrepare(boost::shared_ptr<BFT::order::messages::Prepare> _p)
      { prepareCache.at(_p.get()->getSendingReplica()) = _p; }
						
      inline void Certificate::cacheCommit(boost::shared_ptr<BFT::order::messages::Commit> _c)
      { commitCache.at(_c.get()->getSendingReplica()) = _c; }
			
      inline bool Certificate::isPrePrepared() {return getHistory().use_count() != 0; }	
			
      inline bool Certificate::isClear() {return clear_flag; }
			
    } //namespace statemanagement
  }//namespace order
}//namespace BFT

#endif
